// 读取配置信息库
// 相同配置名生效优先级由低到高为：/etc/localhost.ini -> app.ini -> <app_name>.ini -> conf_path -> cmd args
package config

import (
	"fmt"
	"os"
	"os/signal"
	"path/filepath"
	"reflect"
	"sieve_engine/lib/util/consoletext"
	"sort"
	"strconv"
	"strings"
	"syscall"
)

var (
	cfgMap        = map[string]*cfgItem{}
	fShowUsage    = false
	parsed        = false
	parsedDefVals = ParsedData{}
	parsedList    = parseAll()
	onReload      = []func(){}
)

func parseAll() []ParsedData {
	return []ParsedData{
		parsedDefVals, // 当配置文件中的键值删除后，配置实际值回归默认
		parseIni("/etc/localhost.ini"),
		parseIni(File("app.ini")),
		parseIni(File(filepath.Base(os.Args[0]) + ".ini")),
		parseIni(getConfPath()),
		parseCmdArgs(),
	}
}

func init() {
	parsed = true
	for _, cfg := range cfgMap {
		cfg.parse(parsedList...)
	}

	chReloadSig := make(chan os.Signal)
	signal.Notify(chReloadSig, syscall.SIGHUP)
	go func() {
		for {
			<-chReloadSig
			parsedList = parseAll()
			for _, cfg := range cfgMap {
				cfg.parse(parsedList...)
			}
			for _, fn := range onReload {
				go func(fn func()) {
					defer func() { recover() }()
					fn()
				}(fn)
			}
		}
	}()
}

func OnReload(fn func()) {
	onReload = append(onReload, fn)
}

func add(t, name, usage string, v any, defVals []string) any {
	item := &cfgItem{
		Type:  t,
		Name:  name,
		Usage: usage,
		Value: v,
	}
	if i, ok := cfgMap[name]; ok {
		if i.Type != t {
			panic("Config: redeclared config name `" + name + "` with different type: `" + i.Type + "`,`" + t + "`")
		}
		item.Value = i.Value
		reflect.ValueOf(item.Value).Elem().Set(reflect.ValueOf(v).Elem())
	}
	cfgMap[name] = item

	parsedDefVals[name] = defVals

	if parsed {
		// 在main启动后才试图解析的配置无法出现在参数打印里
		cfgMap[name].parse(parsedList...)
	}

	return item.Value
}

func Get(name string) any {
	if it, ok := cfgMap[name]; ok {
		return it.Value
	}
	return nil
}

// 定义string类型的配置项
func String(name string, value string, usage string) *string {
	return add("string", name, usage, &value, []string{value}).(*string)
}

// 定义[]string类型的配置项
func Strings(name string, value []string, usage string) *[]string {
	return add("strings", name, usage, &value, value).(*[]string)
}

// 定义int类型的配置项
func Int(name string, value int, usage string) *int {
	return add("int", name, usage, &value, []string{strconv.Itoa(value)}).(*int)
}

// 定义[]int类型的配置项
func Ints(name string, value []int, usage string) *[]int {
	ss := []string{}
	for _, n := range value {
		ss = append(ss, strconv.Itoa(n))
	}
	return add("ints", name, usage, &value, ss).(*[]int)
}

// 定义int64类型的配置项
func Int64(name string, value int64, usage string) *int64 {
	return add("int64", name, usage, &value, []string{strconv.FormatInt(value, 10)}).(*int64)
}

// 定义[]int64类型的配置项
func Int64s(name string, value []int64, usage string) *[]int64 {
	ss := []string{}
	for _, n := range value {
		ss = append(ss, strconv.FormatInt(n, 10))
	}
	return add("int64s", name, usage, &value, ss).(*[]int64)
}

// 定义float64类型的配置项
func Float64(name string, value float64, usage string) *float64 {
	return add("float64", name, usage, &value, []string{strconv.FormatFloat(value, 'f', -1, 64)}).(*float64)
}

// 定义[]float64类型的配置项
func Float64s(name string, value []float64, usage string) *[]float64 {
	ss := []string{}
	for _, n := range value {
		ss = append(ss, strconv.FormatFloat(n, 'f', -1, 64))
	}
	return add("float64s", name, usage, &value, ss).(*[]float64)
}

// 定义bool类型的配置项
func Bool(name string, value bool, usage string) *bool {
	return add("bool", name, usage, &value, []string{strconv.FormatBool(value)}).(*bool)
}

// 定义[]bool类型的配置项
func Bools(name string, value []bool, usage string) *[]bool {
	ss := []string{}
	for _, n := range value {
		ss = append(ss, strconv.FormatBool(n))
	}
	return add("bools", name, usage, &value, ss).(*[]bool)
}

// 配置文件目录
func Dir() string {
	return filepath.Join(filepath.Dir(os.Args[0]), "../conf")
}

// 根据文件名返回完整的文件路径
func File(filename string) string {
	return filepath.Join(filepath.Dir(os.Args[0]), "../conf", filename)
}

//

type ParsedData map[string][]string

func (m ParsedData) Add(k, v string) {
	if _, ok := m[k]; !ok {
		m[k] = []string{}
	}
	m[k] = append(m[k], v)
}

func (m ParsedData) Get(k string) (string, bool) {
	return m.GetIndex(k, 0)
}

func (m ParsedData) GetIndex(k string, i int) (string, bool) {
	s, ok := m[k]
	if !ok {
		return "", false
	}
	if i < 0 || i >= len(s) {
		return "", false
	}
	return s[i], true
}

func (m ParsedData) GetAll(k string) ([]string, bool) {
	s, ok := m[k]
	return s, ok
}

//

// func PrintUsage() {
// 	if !fShowUsage {
// 		return
// 	}
// 	fmt.Println("Usage of " + filepath.Base(os.Args[0]))
// 	names := []string{}
// 	for name := range cfgMap {
// 		names = append(names, name)
// 	}
// 	sort.Strings(names)
// 	for _, name := range names {
// 		printItem(cfgMap[name])
// 	}
// 	os.Exit(0)
// }
//
// func printItem(it *cfgItem) {
// 	name := consoletext.ColorText(consoletext.Blue, "--"+strings.Replace(it.Name, ".", "-", -1))
// 	desc := it.Usage
// 	if dv := it.vstr(); dv != "" {
// 		desc += " (current: " + dv + ")"
// 	}
// 	fmt.Println(" ", name, it.Type)
// 	fmt.Println("   ", desc)
// 	fmt.Println("")
// }

func PrintUsage() {
	if !fShowUsage {
		return
	}
	fmt.Println("Usage of " + filepath.Base(os.Args[0]))
	fmt.Print(makeUsage(true))
	os.Exit(0)
}

func DumpUsage() string {
	return makeUsage(false)
}

func makeUsage(colored bool) string {
	s := ""
	var names []string
	for name := range cfgMap {
		names = append(names, name)
	}
	sort.Strings(names)
	for _, name := range names {
		s += makeItemUsage(cfgMap[name], colored)
	}
	return s
}

func makeItemUsage(it *cfgItem, colored bool) string {
	name := "--" + strings.ReplaceAll(it.Name, ".", "-")
	if colored {
		name = consoletext.ColorText(consoletext.Blue, name)
	}
	desc := it.Usage
	if dv := it.vstr(); dv != "" {
		desc += " (current: " + dv + ")"
	}
	s := fmt.Sprintln(" ", name, it.Type)
	s += fmt.Sprintln("   ", desc)
	s += fmt.Sprintln("")
	return s
}

//

type SeqCfg struct {
	name  string
	usage string
}

func Sequence(name string, usage string, walk func(*SeqCfg)) int {
	cnt := *Int(name+".$", 0, usage+" [AUTOGEN]")
	for i := 0; i < cnt; i++ {
		n := strconv.Itoa(i)
		walk(&SeqCfg{name: name + "." + n + ".", usage: usage + " " + n + ": "})
	}
	return cnt
}

func (c *SeqCfg) String(name string, value string, usage string) *string {
	return String(c.name+name, value, c.usage+usage)
}
func (c *SeqCfg) Strings(name string, value []string, usage string) *[]string {
	return Strings(c.name+name, value, c.usage+usage)
}

func (c *SeqCfg) Int(name string, value int, usage string) *int {
	return Int(c.name+name, value, c.usage+usage)
}
func (c *SeqCfg) Ints(name string, value []int, usage string) *[]int {
	return Ints(c.name+name, value, c.usage+usage)
}

func (c *SeqCfg) Int64(name string, value int64, usage string) *int64 {
	return Int64(c.name+name, value, c.usage+usage)
}
func (c *SeqCfg) Int64s(name string, value []int64, usage string) *[]int64 {
	return Int64s(c.name+name, value, c.usage+usage)
}

func (c *SeqCfg) Float64(name string, value float64, usage string) *float64 {
	return Float64(c.name+name, value, c.usage+usage)
}
func (c *SeqCfg) Float64s(name string, value []float64, usage string) *[]float64 {
	return Float64s(c.name+name, value, c.usage+usage)
}

func (c *SeqCfg) Bool(name string, value bool, usage string) *bool {
	return Bool(c.name+name, value, c.usage+usage)
}
func (c *SeqCfg) Bools(name string, value []bool, usage string) *[]bool {
	return Bools(c.name+name, value, c.usage+usage)
}

func (c *SeqCfg) Sequence(name string, usage string, walk func(*SeqCfg)) int {
	return Sequence(c.name+name, c.usage+usage, walk)
}
